package org.hepeng.workx.exception.translate;

import org.hepeng.workx.service.error.ClientError;
import org.hepeng.workx.service.error.Error;
import org.hepeng.workx.service.RestServiceCallResult;
import org.hepeng.workx.service.ServiceCallResult;
import org.hepeng.workx.service.error.ServerError;
import org.hepeng.workx.util.proxy.Invocation;
import org.hepeng.workx.util.proxy.NativeInvoke;
import org.hepeng.workx.util.proxy.Invoker;
import org.springframework.http.HttpStatus;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.UnknownHttpStatusCodeException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.server.MediaTypeNotSupportedStatusException;
import org.springframework.web.server.MethodNotAllowedException;
import org.springframework.web.server.NotAcceptableStatusException;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerErrorException;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
import org.springframework.web.servlet.ModelAndViewDefiningException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.Objects;

/**
 * @author he peng
 */

@RestControllerAdvice
public class RSCRSpringWebLayerExceptionTranslator extends SCRJdkExceptionTranslator
        implements Invoker, SpringWebLayerExceptionTranslator<ResponseEntity<RestServiceCallResult<? extends Error, Object>>> {

    @Override
    public Object invoke(Invocation invocation) throws Throwable {

        Object result = invocation.invoke();
        if (Objects.isNull(result)) {
            RestServiceCallResult body = newBuilder().error(Error.UNKNOWN_ERROR).build();
            result = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body);
        }
        if (! (result instanceof RestServiceCallResult)
                && ServiceCallResult.class.isAssignableFrom(result.getClass())) {
            RestServiceCallResult body = newBuilder().serviceCallResult((ServiceCallResult) result).build();
            result = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body);
        }
        return result;
    }

    @NativeInvoke
    protected RestServiceCallResult.RestServiceCallResultBuilder newBuilder() {
        return new RestServiceCallResult.RestServiceCallResultBuilder<>();
    }

    @NativeInvoke
    protected ResponseEntity<RestServiceCallResult<? extends Error, Object>> buildResponseEntity(
            HttpStatus status , Error error , String msg) {
        return ResponseEntity.status(status).body(newBuilder().serviceCallResult(build(error, msg)).build());
    }

    @NativeInvoke
    protected ResponseEntity<RestServiceCallResult<? extends Error, Object>> buildResponseEntity(
            HttpStatus status , Error error , Throwable t) {
        return ResponseEntity.status(status).body(newBuilder()
                .serviceCallResult(build(error, getRootCauseMsg(t))).build());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpMessageConversionException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.CLIENT_ERROR , ex);
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpMessageNotReadableException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.CLIENT_ERROR , ex);
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpMessageNotWritableException ex) {
        // TODO 搞清楚 HttpMessageNotWritableException 什么情况下抛出
        return null;
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(InvalidMediaTypeException ex) {
        // TODO input , output 的过程中都有可能抛出 InvalidMediaTypeException 异常 , 该如何处理？
        return null;
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(MethodArgumentNotValidException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.PARAMETER_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(MissingPathVariableException ex) {
        return buildResponseEntity(HttpStatus.NOT_FOUND , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(MissingServletRequestParameterException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(ServletRequestBindingException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(UnsatisfiedServletRequestParameterException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.PARAMETER_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpClientErrorException ex) {
        return buildResponseEntity(ex.getStatusCode() , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpServerErrorException ex) {
        return buildResponseEntity(ex.getStatusCode() , ServerError.SERVER_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpStatusCodeException ex) {
        return buildResponseEntity(ex.getStatusCode() , Error.UNKNOWN_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(ResourceAccessException ex) {
        return null;
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(RestClientException ex) {
        return null;
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(RestClientResponseException ex) {
        return null;
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(UnknownHttpStatusCodeException ex) {
        return buildResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR , ServerError.SERVER_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(MaxUploadSizeExceededException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.PARAMETER_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(MultipartException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.PARAMETER_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(MediaTypeNotSupportedStatusException ex) {
        return buildResponseEntity(HttpStatus.UNSUPPORTED_MEDIA_TYPE , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(MethodNotAllowedException ex) {
        return buildResponseEntity(HttpStatus.METHOD_NOT_ALLOWED , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(NotAcceptableStatusException ex) {
        return buildResponseEntity(HttpStatus.NOT_ACCEPTABLE , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(ServerErrorException ex) {
        return buildResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR , ServerError.SERVER_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(ServerWebInputException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(UnsupportedMediaTypeStatusException ex) {
        return buildResponseEntity(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE , ClientError.CLIENT_ERROR , ex.getMessage());
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpMediaTypeException ex) {
        return buildResponseEntity(HttpStatus.BAD_REQUEST , ClientError.CLIENT_ERROR , ex);
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpMediaTypeNotAcceptableException ex) {
        return buildResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR , ServerError.SERVER_ERROR ,
                "the request handler cannot generate a response that is acceptable by the client");
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpMediaTypeNotSupportedException ex) {
        return buildResponseEntity(HttpStatus.UNSUPPORTED_MEDIA_TYPE , ClientError.CLIENT_ERROR , ex);
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpRequestMethodNotSupportedException ex) {
        return buildResponseEntity(HttpStatus.METHOD_NOT_ALLOWED , ClientError.CLIENT_ERROR , ex);
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(HttpSessionRequiredException ex) {
        return null;
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(ModelAndViewDefiningException ex) {
        return null;
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(NoHandlerFoundException ex) {
        return buildResponseEntity(HttpStatus.NOT_FOUND , ClientError.CLIENT_ERROR , ex);
    }

    @Override
    public ResponseEntity<RestServiceCallResult<? extends Error, Object>> translate(ResponseStatusException ex) {
        return null;
    }
}
